﻿Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.Activities.Rules
    <Serializable> _
    Public Class RuleSet
        ' Methods
        Public Sub New()
            Me.behavior = RuleChainingBehavior.Full
            Me.syncLock = New Object
            Me.rules = New List([Of] Rule)
        End Sub

        Public Sub New(ByVal name As String)
            Me.New()
            Me.name = name
        End Sub

        Public Sub New(ByVal name As String, ByVal description As String)
            Me.New(name)
            Me.description = description
        End Sub

        Public Function Clone() As RuleSet
            Dim set As RuleSet = DirectCast(MyBase.MemberwiseClone, RuleSet)
            [set].runtimeInitialized = False
            If (Not Me.rules Is Nothing) Then
                [set].rules = New List([Of] Rule)
                Dim rule As Rule
                For Each rule In Me.rules
                    [set].rules.Add(rule.Clone)
                Next
            End If
            Return [set]
        End Function

        Public Overrides Function Equals(ByVal obj As Object) As Boolean
            Dim set As RuleSet = [TryCast](obj,RuleSet)
            If ([set] Is Nothing) Then
                Return False
            End If
            If (((Me.Name <> [set].Name) OrElse (Me.Description <> [set].Description)) OrElse ((Me.ChainingBehavior <> [set].ChainingBehavior) OrElse (Me.Rules.Count <> [set].Rules.Count))) Then
                Return False
            End If
            Dim i As Integer
            For i = 0 To Me.rules.Count - 1
                If Not Me.rules.Item(i).Equals([set].rules.Item(i)) Then
                    Return False
                End If
            Next i
            Return True
        End Function

        Public Sub Execute(ByVal ruleExecution As RuleExecution)
            If (ruleExecution Is Nothing) Then
                Throw New ArgumentNullException("ruleExecution")
            End If
            If (ruleExecution.Validation Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_MissingValidationProperty"), "ruleExecution")
            End If
            New RuleEngine(Me, ruleExecution.Validation, ruleExecution.ActivityExecutionContext).Execute(ruleExecution)
        End Sub

        Friend Sub Execute(ByVal activity As Activity, ByVal executionContext As ActivityExecutionContext)
            If (activity Is Nothing) Then
                Throw New ArgumentNullException("activity")
            End If
            Dim thisType As Type = activity.GetType
            Dim cachedEngine As RuleEngine = Nothing
            SyncLock Me.syncLock
                If (((Me.cachedEngine Is Nothing) OrElse (Me.cachedValidation Is Nothing)) OrElse (Not Me.cachedValidation.ThisType Is thisType)) Then
                    Dim validation As New RuleValidation(thisType, Nothing)
                    cachedEngine = New RuleEngine(Me, validation, executionContext)
                    Me.cachedValidation = validation
                    Me.cachedEngine = cachedEngine
                Else
                    cachedEngine = Me.cachedEngine
                End If
            End SyncLock
            cachedEngine.Execute(activity, executionContext)
        End Sub

        Public Overrides Function GetHashCode() As Integer
            Return MyBase.GetHashCode
        End Function

        Friend Sub OnRuntimeInitialized()
            SyncLock Me.syncLock
                If Not Me.runtimeInitialized Then
                    Dim rule As Rule
                    For Each rule In Me.rules
                        rule.OnRuntimeInitialized
                    Next
                    Me.runtimeInitialized = True
                End If
            End SyncLock
        End Sub

        Public Function Validate(ByVal validation As RuleValidation) As Boolean
            If (validation Is Nothing) Then
                Throw New ArgumentNullException("validation")
            End If
            Dim dictionary As New Dictionary([Of] String, Object)
            Dim rule As Rule
            For Each rule In Me.rules
                If Not String.IsNullOrEmpty(rule.Name) Then
                    If dictionary.ContainsKey(rule.Name) Then
                        Dim error As New ValidationError(Messages.Error_DuplicateRuleName, &H53F)
                        [error].UserData.Item("ErrorObject") = rule
                        validation.AddError([error])
                    Else
                        dictionary.Add(rule.Name, Nothing)
                    End If
                End If
                rule.Validate(validation)
            Next
            If ((Not validation.Errors Is Nothing) AndAlso (validation.Errors.Count <> 0)) Then
                Return False
            End If
            Return True
        End Function


        ' Properties
        Public Property ChainingBehavior As RuleChainingBehavior
            Get
                Return Me.behavior
            End Get
            Set(ByVal value As RuleChainingBehavior)
                If Me.runtimeInitialized Then
                    Throw New InvalidOperationException(SR.GetString("Error_CanNotChangeAtRuntime"))
                End If
                Me.behavior = value
            End Set
        End Property

        Public Property Description As String
            Get
                Return Me.description
            End Get
            Set(ByVal value As String)
                If Me.runtimeInitialized Then
                    Throw New InvalidOperationException(SR.GetString("Error_CanNotChangeAtRuntime"))
                End If
                Me.description = value
            End Set
        End Property

        Public Property Name As String
            Get
                Return Me.name
            End Get
            Set(ByVal value As String)
                If Me.runtimeInitialized Then
                    Throw New InvalidOperationException(SR.GetString("Error_CanNotChangeAtRuntime"))
                End If
                Me.name = value
            End Set
        End Property

        <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
        Public ReadOnly Property Rules As ICollection([Of] Rule)
            Get
                Return Me.rules
            End Get
        End Property


        ' Fields
        Friend behavior As RuleChainingBehavior
        <NonSerialized> _
        Private cachedEngine As RuleEngine
        <NonSerialized> _
        Private cachedValidation As RuleValidation
        Friend description As String
        Friend name As String
        Friend rules As List([Of] Rule)
        Friend Const RuleSetTrackingKey As String = "RuleSet."
        Private runtimeInitialized As Boolean
        Private [syncLock] As Object
    End Class
End Namespace

